﻿using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace ObjectExample
{
    internal class MethodRefFromSSDP
    {
        public static void DoStatistic()
        {
            Assembly toDLL = null;
            Assembly fromDLL = null;

            var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in loadedAssemblies)
            {
                if (assembly.GetName().Name == "ObjectExample")
                {
                    toDLL = assembly;
                    continue;
                }

                if (assembly.GetName().Name == "ATND")
                {
                    fromDLL = assembly;
                    continue;
                }
            }

            List<String> ssdpDLLs = GrepRefSSDPDLLs();
            List<String> userDLLs = SlnDLLs();

            var notLoaded_SSDP_DLL = ssdpDLLs.Where(r => !loadedAssemblies.Any(a => a.GetName().Name == r)).ToList();
            var notLoaded_User_DLL = userDLLs.Where(r => !loadedAssemblies.Any(a => a.GetName().Name == r)).ToList();


            var loaded_SSDP_DLL = loadedAssemblies.Where(a => ssdpDLLs.Any(s => s == a.GetName().Name)).ToList();
            var loaded_User_DLL = loadedAssemblies.Where(a => userDLLs.Any(s => s == a.GetName().Name)).ToList();


            var usedSSDPs = new HashSet<UsedMethod_SSDP>();
            foreach (var u in loaded_User_DLL)
            {
                var L = StaticsSymbolsInExample(u);
                usedSSDPs.UnionWith(L);
            }

            // Output the results
            var f = "./xue.ssdp.method.txt";
            ToTxt(usedSSDPs, f);
            MessageBox.Show($"统计结果保存为\n:{f}");

            // MsgBox
            var msg = string.Join("\n", notLoaded_User_DLL);
            MessageBox.Show($"未加载的DLL，点击相关例子后重新统计\n:{msg}");
        }

        public static void ToTxt(HashSet<UsedMethod_SSDP> usedSSDPs, string filePath)
        {
            using (StreamWriter writer = new StreamWriter(filePath))
            {
                // Group by DllName
                var groupedByDll = usedSSDPs.GroupBy(m => m.DllName);

                foreach (var dllGroup in groupedByDll)
                {
                    writer.WriteLine($"{dllGroup.Key}"); // DllName

                    // Group by TypeName within each Dll
                    var groupedByTypeName = dllGroup.GroupBy(m => m.TypeName);

                    foreach (var typeGroup in groupedByTypeName)
                    {
                        writer.WriteLine($"\t{typeGroup.Key}"); // TypeName

                        // Write MethodNames
                        foreach (var method in typeGroup)
                        {
                            writer.WriteLine($"\t\t{method.MethodName}"); // MethodName
                        }
                    }
                }
            }
        }

        private static List<String> GrepRefSSDPDLLs()
        {
            // rg "AT\w+\.dll" --glob *.csproj | grep -oP "AT\w+\.dll" | sort | uniq

            return new List<String>()
            {
                "ATBNG",
                "ATGONET",
                "ATGP",
                "ATMOC",
                "ATNCL",
                "ATND",
                "ATNG",
                "ATNHS",
                "ATNOC",
                "ATNP",
                "ATRC",
                "ATROC",
                "ATRP",
            };
        }

        private static List<String> SlnDLLs()
        {
            // find . -type f -name "*.csproj" -exec basename {} .csproj \; | sed 's/^/"/; s/$/",/'
            var R = new List<String>()
            {
                "ATLoadNet",
                "ATSceneExample",
                "CCFWorkExample",
                "DimensionExample",
                "EntityExample",
                "DynamicButtonsUI",
                "ExampleTestUI",
                "GeometryExample",
                "LevelExample",
                "MaterialExample",
                "MeshExample",
                "ObjectExample",
                "ParameterizedObjectExample",
                "PatternExample",
                //"PrimitiveGeoExample",
                "ProjectExample",
                "ProjectTreeExample",
                "PropsItemExample",
                "SharedOUnitExample",
                "SZEWAlignmentUtilExample",
                "SZEWBridgeUtilExample",
                "SZEWComponentLibraryUtilExample",
                "SZEWTerrainUtilExample",
                "SZEWTunnelUtilExample",
                "TextExample",
                "TextTableExample",
                "CmdExample",
                "ViewExample",

            };
            return R;
        }


        private static HashSet<UsedMethod_SSDP> StaticsSymbolsInExample(Assembly userDLL)
        {
            var usedSSDPs = new HashSet<UsedMethod_SSDP>();
            var monoAssm = AssemblyDefinition.ReadAssembly(userDLL.Location);

            List<String> ssdpDLLs = GrepRefSSDPDLLs();

            // Iterate over all types in the assembly
            foreach (var type in monoAssm.MainModule.Types)
            {
                //// field
                //var allFd = type.Fields;
                //foreach (var field in allFd)
                //{
                //    try
                //    {
                //        var nm = field.Resolve()?.Module.Assembly.Name.Name;
                //        if (ssdpDLLs.Contains(nm))
                //        {
                //            var t = field.FieldType.FullName;
                //            usedThirdPartyTypes.Add(t);
                //        }
                //    }
                //    catch (Exception)
                //    {
                //        continue;
                //    }
                //}

                //// properties
                //var allP = type.Fields;
                //foreach (var p in allP)
                //{
                //    try
                //    {
                //        var nm = p.Resolve()?.Module.Assembly.Name.Name;
                //        if (ssdpDLLs.Contains(nm))
                //        {
                //            var t = p.FieldType.FullName;
                //            usedThirdPartyTypes.Add(t);
                //        }
                //    }
                //    catch (Exception)
                //    {
                //        continue;
                //    }
                //}

                // methods
                var allMethod = type.Methods;
                foreach (var method in allMethod)
                {
                    // return type
                    //if (method.ReturnType is Mono.Cecil.TypeReference r)
                    //{
                    //    try
                    //    {
                    //        var nm = r.Resolve()?.Module.Assembly.Name.Name;
                    //        if (ssdpDLLs.Contains(nm))
                    //        {
                    //            var t = r.FullName;
                    //            usedThirdPartyTypes.Add(t);
                    //        }
                    //    }
                    //    catch (Exception)
                    //    {
                    //        continue;
                    //    }
                    //}
                    //// parameter type
                    //foreach (var p in method.Parameters)
                    //{
                    //    try
                    //    {
                    //        var nm = p.ParameterType.Resolve()?.Module.Assembly.Name.Name;
                    //        if (ssdpDLLs.Contains(nm))
                    //        {
                    //            var t = p.ParameterType.FullName;
                    //            usedThirdPartyTypes.Add(t);
                    //        }
                    //    }
                    //    catch (Exception)
                    //    {
                    //        continue;
                    //    }
                    //}

                    // body
                    if (method.Body == null) { continue; }

                    // Get the IL processor
                    var il = method.Body.GetILProcessor();
                    // Iterate over all instructions in the method body
                    foreach (var instruction in il.Body.Instructions)
                    {
                        var op = instruction.Operand;

                        try
                        {
                            //// type reference
                            //if (op is TypeReference tr)
                            //{
                            //    var nm = tr.Resolve()?.Module.Assembly.Name.Name;
                            //    if (ssdpDLLs.Contains(nm))
                            //    {
                            //        usedThirdPartyTypes.Add(tr.FullName);
                            //    }
                            //}

                            if (op is MethodReference mr)
                            {
                                var nm = mr.Resolve()?.Module.Assembly.Name.Name;
                                if (ssdpDLLs.Contains(nm))
                                {

                                    if (mr.Resolve().IsPublic && Char.IsUpper(mr.Name[0]))
                                    {
                                        var x = new UsedMethod_SSDP()
                                        {
                                            DllName = nm,
                                            TypeName = mr.DeclaringType.FullName,
                                            MethodName = mr.Name,
                                        };
                                        usedSSDPs.Add(x);
                                    }
                                }
                            }

                            //// Var definition
                            //if (op is VariableDefinition varDefine)
                            //{
                            //    var nm = varDefine.VariableType.Resolve()?.Module.Assembly.Name.Name;
                            //    if (ssdpDLLs.Contains(nm))
                            //    {
                            //        var t = varDefine.VariableType.FullName;
                            //        usedThirdPartyTypes.Add(t);
                            //    }
                            //}

                            // method definiton
                            //if (op is MethodDefinition md)
                            //{
                            //    var nm = md.ReturnType.Resolve()?.Module.Assembly.Name.Name;
                            //    if (ssdpDLLs.Contains(nm))
                            //    {
                            //        var t = md.ReturnType.FullName;
                            //        usedThirdPartyTypes.Add(t);
                            //    }
                            //}
                        }
                        catch (Exception)
                        {
                            continue;
                        }
                    }
                }
            }

            return usedSSDPs;
        }
    }
    public class UsedMethod_SSDP
    {
        public string DllName { get; set; } = "NoInit";
        public string TypeName { get; set; } = "NoInit";
        public string MethodName { get; set; } = "NoInit";

        public override bool Equals(object obj)
        {
            if (obj == null || GetType() != obj.GetType())
            {
                return false;
            }

            UsedMethod_SSDP other = (UsedMethod_SSDP)obj;
            return DllName == other.DllName && TypeName == other.TypeName && MethodName == other.MethodName;
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 23 + (DllName?.GetHashCode() ?? 0);
                hash = hash * 23 + (TypeName?.GetHashCode() ?? 0);
                hash = hash * 23 + (MethodName?.GetHashCode() ?? 0);
                return hash;
            }
        }
    }
}
